package cn.amossun.starter.mybatis.data.security.config;

import cn.amossun.starter.common.constant.Constant;
import cn.amossun.starter.mybatis.data.security.interceptor.*;
import cn.amossun.starter.mybatis.data.security.loader.DecryptRuleCacheLoader;
import cn.amossun.starter.mybatis.data.security.loader.OrmConfigCacheLoader;
import cn.amossun.starter.mybatis.data.security.rewrite.DecryptPreParameterRewriter;
import cn.amossun.starter.mybatis.data.security.rewrite.EncryptPreParameterRewriter;
import cn.amossun.starter.mybatis.data.security.rule.MybatisDecryptRule;
import cn.amossun.starter.mybatis.data.security.rule.MybatisEncryptRule;
import cn.amossun.starter.mybatis.data.security.runner.DecryptRuleLocalCacheRunner;
import cn.amossun.starter.mybatis.data.security.runner.OrmConfigCacheRunner;
import cn.amossun.starter.redis.cache.properties.RedisProperties;
import lombok.AllArgsConstructor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.security.Security;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Mybatis拦截器
 * Mybatis只能拦截指定类里面的方法：Executor、ParameterHandler、StatementHandler、ResultSetHandler。
 * Executor：拦截执行器方法；
 * ParameterHandler：拦截参数方法；
 * StatementHandler：拦截sql构建方法；
 * ResultSetHandler：拦截查询结果方法；
 * http://t.zoukankan.com/zhjh256-p-11516878.html
 * https://blog.csdn.net/qq_38225558/article/details/85018810
 */
@ConditionalOnProperty(
        prefix = Constant.AMOS_SUN_DATA_SECURITY,
        name = "mybatisPlus.enabled",
        havingValue = "true"
)
@AllArgsConstructor
@EnableConfigurationProperties(MybatisPlusDataSecurityConfig.class)
@Configuration
public class MybatisDataSecurityConfigure {

    @Bean(name = "amosThreadPoolTaskExecutor")
    public ThreadPoolTaskExecutor amosThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(2);
        //配置最大线程数
        executor.setMaxPoolSize(4);
        //配置队列大小
        executor.setQueueCapacity(100);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("amos-starter-async-");

        // rejection-policy：当pool已经达到max size的时候，如何处理新任务
        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }

    @Bean
    @ConditionalOnMissingBean(DecryptRuleCacheLoader.class)
    public DecryptRuleCacheLoader decryptRuleCacheLoader(@Autowired  @Qualifier("amosThreadPoolTaskExecutor") ThreadPoolTaskExecutor amosThreadPoolTaskExecutor,
                                                         OrmConfigCacheRunner ormConfigCacheRunner,
                                                         MybatisDecryptRule mybatisDecryptRule) {
        return new DecryptRuleCacheLoader(amosThreadPoolTaskExecutor, ormConfigCacheRunner, mybatisDecryptRule);
    }

    @Bean
    @ConditionalOnMissingBean(DecryptRuleLocalCacheRunner.class)
    public DecryptRuleLocalCacheRunner decryptRuleLocalCacheRunner(MybatisPlusDataSecurityConfig mybatisConfig,
                                                                   MybatisDecryptRule mybatisDecryptRule,
                                                                   DecryptRuleCacheLoader decryptRuleCacheLoader,
                                                                   OrmConfigCacheRunner ormConfigCacheRunner) {
        return new DecryptRuleLocalCacheRunner(mybatisConfig, mybatisDecryptRule, decryptRuleCacheLoader, ormConfigCacheRunner);
    }

    @Bean
    @ConditionalOnMissingBean(OrmConfigCacheLoader.class)
    public OrmConfigCacheLoader ormConfigCacheLoader(@Autowired(required = false) @Qualifier("amosRedisTemplate") RedisTemplate amosRedisTemplate,
                                                     @Autowired  @Qualifier("amosThreadPoolTaskExecutor") ThreadPoolTaskExecutor amosThreadPoolTaskExecutor) {
        return new OrmConfigCacheLoader(amosRedisTemplate, amosThreadPoolTaskExecutor);
    }

    @Bean
    @ConditionalOnMissingBean(OrmConfigCacheRunner.class)
    public OrmConfigCacheRunner ormConfigCacheRunner(RedisProperties redisProperties, MybatisPlusDataSecurityConfig mybatisConfig, OrmConfigCacheLoader ormConfigCacheLoader) {
        return new OrmConfigCacheRunner(redisProperties, mybatisConfig, ormConfigCacheLoader);
    }

    @Bean
    @ConditionalOnMissingBean(BouncyCastleProvider.class)
    public BouncyCastleProvider addBouncyCastleProvider(){
        BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
        Security.addProvider(bouncyCastleProvider);
        return bouncyCastleProvider;
    }

    @Bean
    @ConditionalOnClass({MybatisPlusDataSecurityConfig.class, OrmConfigCacheRunner.class})
    public MybatisEncryptRule mybatisEncryptRule(MybatisPlusDataSecurityConfig mybatisConfig, OrmConfigCacheRunner remoteCache) throws Exception {
        return new MybatisEncryptRule(mybatisConfig, remoteCache);
    }

    @Bean
    @ConditionalOnClass({MybatisPlusDataSecurityConfig.class, OrmConfigCacheRunner.class})
    public MybatisDecryptRule mybatisDecryptRule(MybatisPlusDataSecurityConfig mybatisConfig, OrmConfigCacheRunner remoteCache) throws Exception {
        return new MybatisDecryptRule(mybatisConfig, remoteCache);
    }

    @Bean
    @ConditionalOnClass({MybatisEncryptRule.class})
    public EncryptPreParameterRewriter encryptPreParameterRewriter(BouncyCastleProvider bouncyCastleProvider, MybatisEncryptRule mybatisEncryptRule) throws Exception {
        return new EncryptPreParameterRewriter(mybatisEncryptRule);
    }

    @Bean
    @ConditionalOnClass({MybatisDecryptRule.class})
    public DecryptPreParameterRewriter decryptPreParameterRewriter(BouncyCastleProvider bouncyCastleProvider, MybatisDecryptRule mybatisDecryptRule) throws Exception {
        return new DecryptPreParameterRewriter(mybatisDecryptRule);
    }

    @Bean
    @ConditionalOnClass({EncryptPreParameterRewriter.class})
    ParameterHandlerInterceptor parameterHandlerInterceptor(EncryptPreParameterRewriter encryptRewriter){
        return new ParameterHandlerInterceptor();
    }

    @Bean
    @ConditionalOnClass({EncryptPreParameterRewriter.class})
    StatementHandlerInterceptor statementHandlerInterceptor(EncryptPreParameterRewriter encryptRewriter){
        return new StatementHandlerInterceptor(encryptRewriter);
    }

    @Bean
    @ConditionalOnClass({EncryptPreParameterRewriter.class, DecryptPreParameterRewriter.class})
    ExecutorInterceptor executorInterceptor(EncryptPreParameterRewriter encryptRewriter,
                                            DecryptPreParameterRewriter decryptRewriter){
        return new ExecutorInterceptor();
    }

    @Bean
    @ConditionalOnClass({EncryptPreParameterRewriter.class, DecryptPreParameterRewriter.class})
    ResultSetHandlerInterceptor resultSetHandlerInterceptor(DecryptPreParameterRewriter decryptRewriter){
        return new ResultSetHandlerInterceptor(decryptRewriter);
    }
}
